﻿using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace WxLib.Core.Util
{
    /// <summary>
    ///     Helper class for performing web requests
    /// </summary>
    public static class HttpRequestHelper
    {
        /// <summary>
        ///     Default content type
        /// </summary>
        public const string ContentType = "application/x-www-form-urlencoded";

        private static readonly TraceSource Trace = new TraceSource("WxLib");

        /// <summary>
        /// 远程证书验证回调方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="certificate"></param>
        /// <param name="chain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <returns></returns>
        public static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;
            return false;
        }

        /// <summary>
        ///     Creates a new HttpWebRequest for the specified URI, and returns it
        /// </summary>
        /// <param name="uri">URI to create request for</param>
        /// <returns>The HttpWebRequest</returns>
        public static HttpWebRequest CreateRequest(Uri uri)
        {
            //设置https验证方式
            if (uri.Scheme == "https")
            {
                ServicePointManager.DefaultConnectionLimit = 200;
                ServicePointManager.ServerCertificateValidationCallback =
                        new RemoteCertificateValidationCallback(RemoteCertificateValidationCallback);
            }
            var request = WebRequest.CreateHttp(uri);
            request.UserAgent = Config.UserAgent;
            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.GZip;
            request.ContentType = ContentType;

            return request;
        }

        /// <summary>
        ///     Performs a web request against the specified URI, and returns the response content
        /// </summary>
        /// <param name="uri">URI to request</param>
        /// <returns>The response content</returns>
        public static Task<string> RequestAsync(Uri uri)
        {
            var request = CreateRequest(uri);
            return GetResponseContentAsync(request);
        }

        /// <summary>
        ///  针对指定的URI执行Web请求，并返回响应内容
        /// </summary>
        /// <param name="uri">请求地址</param>
        /// <param name="postData">提交的数据</param>
        /// <returns>响应内容</returns>
        public static Task<string> PostRequestAsync(Uri uri, string postData)
        {
            var request = CreateRequest(uri);
            request.Method = "POST";
            AddPostData(request, postData);
            return GetResponseContentAsync(request);
        }

        public static Task<string> PostRequestAsync(Uri uri, byte[] postData)
        {
            var request = CreateRequest(uri);
            request.Method = "POST";
            AddPostData(request, postData);
            return GetResponseContentAsync(request);
        }

        /// <summary>
        /// 针对指定的URI执行带证书Web请求，并返回响应内容
        /// </summary>
        /// <param name="uri">请求地址</param>
        /// <param name="postData">提交的数据</param>
        /// <param name="certPath">证书物理路径</param>
        /// <param name="certPassword">证书密码</param>
        /// <returns>响应内容</returns>
        public static Task<string> PostRequestAsync(Uri uri, string postData, string certPath, string certPassword)
        {
            var request = CreateRequest(uri);
            request.Method = "POST";
            X509Certificate2 cert = new X509Certificate2(certPath, certPassword);
            request.ClientCertificates.Add(cert);
            AddPostData(request, postData);
            return GetResponseContentAsync(request);
        }

        /// <summary>
        ///  添加提交数据
        /// </summary>
        /// <param name="request">请求</param>
        /// <param name="postData">数据</param>
        public static void AddPostData(HttpWebRequest request, string postData)
        {
            if (postData == null) return;
            var data = Encoding.UTF8.GetBytes(postData);
            AddPostData(request, data);
            //using (var writer = new StreamWriter(request.GetRequestStream())) {
            //    writer.Write(postData);
            //}
        }

        public static void AddPostData(HttpWebRequest request, byte[] data)
        {
            if (data == null) return;
            request.ContentLength = data.Length;
            using (var dataStream = request.GetRequestStream())
            {
                dataStream.Write(data, 0, data.Length);
            }
            //using (var writer = new StreamWriter(request.GetRequestStream())) {
            //    writer.Write(postData);
            //}
        }

        /// <summary>
        ///  使用给定的请求执行Web请求，并返回HttpWebResponse
        /// </summary>
        /// <param name="request">请求</param>
        /// <returns></returns>
        public static async Task<HttpWebResponse> GetResponseAsync(HttpWebRequest request)
        {
            HttpWebResponse response;
            try
            {
                response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false);
                if (response != null)
                {
                    Trace.TraceEvent(TraceEventType.Information, 0,
                        "Response status: " + response.StatusCode + ", " + response.StatusDescription);
                    Trace.TraceEvent(TraceEventType.Verbose, 0, "From cache: " + response.IsFromCache);
                }
            }
            catch (WebException e)
            {
                response = (HttpWebResponse)e.Response;
                if (response == null) throw;
                Trace.TraceEvent(TraceEventType.Information, 0,
                    "Response status: " + response.StatusCode + ", " + response.StatusDescription);
                Trace.TraceEvent(TraceEventType.Verbose, 0, "From cache: " + response.IsFromCache);
                throw;
            }
            return response;
        }

        /// <summary>
        ///  提取并返回HttpWebResponse的响应内容
        /// </summary>
        /// <param name="response">The HttpWebResponse</param>
        /// <returns></returns>
        public static async Task<string> GetResponseContentAsync(HttpWebResponse response)
        {
            string data;
            var responseStream = response.GetResponseStream();
            if (responseStream == null) return null;
            using (var reader = new StreamReader(responseStream))
            {
                data = await reader.ReadToEndAsync().ConfigureAwait(false);
            }
            return data;
        }

        /// <summary>
        /// 执行，提取并返回HttpWebRequest的响应字符内容
        /// </summary>
        /// <param name="request">The HttpWebRequest</param>
        /// <returns></returns>
        public static async Task<string> GetResponseContentAsync(HttpWebRequest request)
        {
            Trace.TraceEvent(TraceEventType.Start, 0, "Starting request: " + request.RequestUri);
            var data = "";
            using (var response = await GetResponseAsync(request).ConfigureAwait(false))
            {
                var responseStream = response.GetResponseStream();
                if (responseStream == null) return data;
                using (var reader = new StreamReader(responseStream))
                {
                    data = await reader.ReadToEndAsync().ConfigureAwait(false);
                }
            }
            Trace.TraceEvent(TraceEventType.Stop, 0, "Finished request: " + request.RequestUri);
            return data;
        }

        /// <summary>
        /// 执行，提取并返回HttpWebRequest的响应二进制流
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static async Task<byte[]> GetResponseBufferAsync(HttpWebRequest request)
        {
            Trace.TraceEvent(TraceEventType.Start, 0, "Starting request: " + request.RequestUri);
            byte[] buffer;
            using (var response = await GetResponseAsync(request).ConfigureAwait(false))
            {
                using (var responseStream = response.GetResponseStream())
                {
                    if (responseStream == null) return null;
                    buffer = new Byte[response.ContentLength];
                    int offset = 0, actuallyRead = 0;
                    do
                    {
                        actuallyRead = responseStream.Read(buffer, offset, buffer.Length - offset);
                        offset += actuallyRead;
                    }
                    while (actuallyRead > 0);
                }
            }
            Trace.TraceEvent(TraceEventType.Stop, 0, "Finished request: " + request.RequestUri);
            return buffer;
        }
    }
}